/*
    Coder: Dzlua
    Email: 505544956@qq.com
    Time : 2017/06/23
*/
#include <string>
#include <memory>

namespace tg {

class HttpsPkg {
public:
    static std::string GetServerName(const char* data) {
        HttpsPkg hsp;
        if (!hsp.Init(data)) return std::move(std::string());
        auto dat = hsp.GetExtension();
        dat = hsp.FindExtention(dat, EXT_SERVER_NAME);
        auto sn = hsp.ParserSerName(dat);
        return std::move(sn->name);
    }
public:
    enum eExtension {
        EXT_SERVER_NAME          = 0x0000,
        EXT_STATUS_REQUEST       = 0x0005,
        EXT_ELLIPTIC_CURVES      = 0x000A,
        EXT_EC_POINT_FORMATS     = 0x000B,
        EXT_SIGNATURE_ALGORITHMS = 0x000D,
        EXT_ALPN                 = 0x0010, //Application Layer Protocol Negotiation (0x0010)
        EXT_SCT                  = 0x0012, //Type: signed_certificate_timestamp (0x0012)
        EXT_PADDING              = 0x0015,
        EXT_MASTER_SECRET        = 0x0017,
        EXT_TOKEN_BINDING        = 0x0018,
        EXT_SESSION_TICKET       = 0x0023,
        EXT_CHANNEL_ID           = 0x7550,
        EXT_RENEGOTIATION        = 0xFF01,
    };

    struct sData {
        const char* p;
        size_t l;
    };

    struct sSerName {
        size_t type;
        std::string name;
    };
    
public:
    HttpsPkg() : m_data(nullptr) {}
    ~HttpsPkg() {}
    bool Init(const char* data) {
        if (data[0] != 0x16)
            return false;
        m_data = data;
        return true;
    }

    sData GetVer() {
        sData dat;
        dat.p = m_data + 1;
        dat.l = 2;
        return dat;
    }

    size_t GetPkgLen() {
        return _GetLen(m_data + 3, 2);
    }

    sData GetClientHello() {
        sData dat;
        dat.l = _GetLen(m_data + 3, 2);
        dat.p = m_data + 5;
        return dat;
    }

    const char* GetHandShakeType() {
        return (m_data + 5);
    }

    size_t GetClientHelloLen() {
        return _GetLen(m_data + 6, 3);
    }

    sData GetClientHelloVer() {
        sData dat;
        dat.p = m_data + 9;
        dat.l = 2;
        return dat;
    }

    sData GetClientHelloRandom() {
        sData dat;
        dat.p = m_data + 11;
        dat.l = 32;
        return dat;
    }

    sData GetClientHelloSession() {
        sData dat;
        dat.p = m_data + 44;//43 is length
        dat.l = _GetLen(m_data + 43, 1);
        return dat;
    }

    sData GetClientHelloCipherSuite() {
        sData dat = GetClientHelloSession();
        dat.p += dat.l + 2;
        dat.l = _GetLen(dat.p - 2, 2);
        return dat;
    }

    sData GetClientHelloCMethod() {
        sData dat = GetClientHelloCipherSuite();
        dat.p += dat.l + 1;
        dat.l = _GetLen(dat.p - 1, 1);
        return dat;
    }

    sData GetExtension() {
        sData dat = GetClientHelloCMethod();
        dat.p += dat.l + 2;
        dat.l = _GetLen(dat.p - 2, 2);
        return dat;
    }

    sData FindExtention(const sData& ext, eExtension type) {
        sData dat = {0};
        dat.p = ext.p;
        for (; dat.p + 4 < ext.p + ext.l;) {
            if ((size_t)type == _GetLen(dat.p, 2)) {
                dat.l = _GetLen(dat.p + 2, 2);
                dat.p += 4;
                return dat;
            }
            dat.p += 4 + _GetLen(dat.p + 2, 2);
        }
        return dat;
    }
public:
    std::shared_ptr<sSerName> ParserSerName(const sData& dat) {
        if (dat.l < 2) return nullptr;
        size_t len = _GetLen(dat.p, 2);
        if (len < 3) return nullptr;
        auto sn = std::make_shared<sSerName>();
        sn->type = _GetLen(dat.p + 2, 1);
        len = _GetLen(dat.p + 3, 2);
        sn->name = std::string(dat.p + 5, len);
        return sn;
    }
public:
    size_t _GetLen(const char* p, size_t len) {
        size_t sz = 0;
        len *= 2;
        for (size_t i = 0; i < len; ++i) {
            size_t a = (i%2 == 0) ? _high4bit(p[i/2]) : _low4bit(p[i/2]);
            if (a == 0) continue;
            size_t b = _powser(16, len - i - 1);
            sz += a * b;
        }
        return sz;
    }

    size_t _powser(size_t x, size_t a) {
        size_t pw = 1;
        for (size_t i = 0; i < a; ++i) {
            pw *= x;
        }
        return pw;
    }

    size_t _high4bit(const char ch) {
        return (ch >> 4) & 0x0f;
    }

    size_t _low4bit(const char ch) {
        return ch & 0x0f;
    }
private:
    const char* m_data;
};

class HttpPkg {
public:
    static std::string GetServerName(const char* data, uint16_t len) {
        int strpos = -1;
		for (int i = 0; i < len - 4; i++) {
			if (data[i] == 'H' 
                    && data[i + 1] == 'o' 
                    && data[i + 2] == 's' 
                    && data[i + 3] == 't') {
				strpos = i;
				break;
			}
		}
		if (strpos > 0) {
			strpos += 6;
		}
		int endpos = strpos;
		while (endpos < len) {
			if (data[endpos] == 0x0d 
                    || data[endpos] == ':') {
				break;
			}
			endpos++;
		}
		if ((endpos - strpos) <= 1000) {
            return std::move(std::string(data + strpos, endpos - strpos));
		}
        return std::move(std::string());
    }
};

static std::string GetServerName(const char* data, uint16_t len) {
    if (0x16 == data[0]) {
        return HttpsPkg::GetServerName(data);
    } else {
        return HttpPkg::GetServerName(data, len);
    }
}

} // end namespace